//
//  FQDRefreshHeader.m
//  FakeQDaily
//
//  Created by mewxiv on 8/21/16.
//  Copyright © 2016 Xensius. All rights reserved.
//

#import "FQDRefreshHeader.h"
#import <objc/message.h>

CGFloat const FQDRefreshPullDownAnimationDuration = 0.3f;
CGFloat const FQDRefreshPullBackAnimationDuration = 0.3f;

static CGFloat const FQDRefreshViewHeight = 120.0f;

static NSString * const FQDKeyPathScrollViewContentOffset = @"contentOffset";
static NSString * const FQDKeyPathScrollViewPanState = @"state";

@interface FQDRefreshHeader ()

@property (nonatomic, strong) UIPanGestureRecognizer *pan;
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL action;
@property (nonatomic, copy) QDRefreshingBlock refreshingBlock;

@property (nonatomic, assign, readwrite) BOOL isRefreshing;
@property (nonatomic, assign, readwrite) CGFloat progress;

@end

@implementation FQDRefreshHeader

#pragma mark - Convenience methods
+ (instancetype)headerWithTarget:(id)target action:(SEL)action
{
    FQDRefreshHeader *header = [[self alloc] init];
    header.target = target;
    header.action = action;
    return header;
}

+ (instancetype)headerWithRefreshingBlock:(QDRefreshingBlock)refreshingBlock
{
    FQDRefreshHeader *header = [[self alloc] init];
    header.refreshingBlock = refreshingBlock;
    return header;
}

#pragma mark - Initializers
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _isRefreshing = NO;
        _progress = 0.0f;
        _refreshHeight = FQDRefreshViewHeight;
        
        [self prepareUI];
        
    }
    return self;
}


#pragma mark - View life cycle
- (void)willMoveToSuperview:(UIView *)newSuperview
{
    [super willMoveToSuperview:newSuperview];
    
    
    UIView *oldSuperview = self.superview;
    if ([oldSuperview isKindOfClass:[UIScrollView class]]) {
        [self removeObservers:oldSuperview];
    }
    if ([newSuperview isKindOfClass:[UIScrollView class]]) {
        
        _scrollView = (UIScrollView *)newSuperview;
        [self prepareLayoutWithFrame:newSuperview.frame];
        
        [self addObservers:newSuperview];
    }
}

#pragma mark - Public methods
- (void)beginRefreshing
{
    [self willBeginRefreshing];
    
    self.isRefreshing = YES;

    [UIView animateWithDuration:FQDRefreshPullDownAnimationDuration animations:^{
        
        UIEdgeInsets inset = self.scrollView.contentInset;
        inset.top += self.refreshHeight;
        self.scrollView.contentInset = inset;
        
    } completion:^(BOOL finished) {
        [self didBeginRefreshing];
    }];

    
}

- (void)endRefreshing
{
    [self willEndRefreshing];
    
    if (!self.isRefreshing) {
        return;
    }
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        self.isRefreshing = NO;
        
        void (^animationsBlock)() = ^{
            
            UIEdgeInsets inset = self.scrollView.contentInset;
            inset.top -= self.refreshHeight;
            self.scrollView.contentInset = inset;
            
        };
        
        void (^completionBlock)(BOOL finished) = ^(BOOL finished) {
            [self didEndRefreshing];
        };
        
        [UIView animateWithDuration:FQDRefreshPullBackAnimationDuration
                              delay:0
                            options:UIViewAnimationOptionCurveEaseIn
                         animations:animationsBlock
                         completion:completionBlock];
    });
    
}

#pragma mark - Override methods

- (void)prepareUI
{
    
}

- (void)prepareLayoutWithFrame:(CGRect)frame
{
    CGRect rect = self.frame;
    rect.size.width = CGRectGetWidth(frame);
    rect.size.height = 0;
    self.frame = rect;
}

- (void)animateDraggingWithProgress:(CGFloat)progress
{
    
}

- (void)willBeginRefreshing
{
    
}

- (void)didBeginRefreshing
{
    
}

- (void)willEndRefreshing
{
    
}

- (void)didEndRefreshing
{
    
}

#pragma mark - Private methods

- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change
{
    CGFloat offsetY = -(self.scrollView.contentOffset.y + self.scrollView.contentInset.top);
    
    self.progress = MAX(offsetY / self.refreshHeight, 0);
    
    if (!self.isRefreshing) {
        [self animateDraggingWithProgress:self.progress];
    }
    
}

- (void)updateHeightWithProgress:(CGFloat)progress
{
    CGRect frame = self.frame;
    frame.size.height = self.refreshHeight * progress;
    self.frame = frame;
}

- (void)scrollViewWillEndDragging
{
    if (!self.isRefreshing && self.progress >= 1.0f) {
        [self beginRefreshing];
        
        if (self.refreshingBlock) {
            self.refreshingBlock(self);
        }
        
        if (self.target && self.action) {
            typedef void (*MsgSendType) (id, SEL);
            ((MsgSendType)objc_msgSend)(self.target, self.action);
        }
    }
}

- (void)scrollViewPanStateDidChange:(NSDictionary *)change
{
    UIGestureRecognizerState state = self.scrollView.panGestureRecognizer.state;
    if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled) {
        [self scrollViewWillEndDragging];
    }
}

#pragma mark - KVO methods
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if (!self.userInteractionEnabled) {
        return;
    }
    
    if ([keyPath isEqualToString:FQDKeyPathScrollViewContentOffset]) {
        [self scrollViewContentOffsetDidChange:change];
    }
    else if ([keyPath isEqualToString:FQDKeyPathScrollViewPanState]) {
        [self scrollViewPanStateDidChange:change];
    }
}

- (void)addObservers:(UIView *)newSuperview
{
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
    UIScrollView *scrollView = (UIScrollView *)newSuperview;
    [scrollView addObserver:self forKeyPath:FQDKeyPathScrollViewContentOffset options:options context:nil];
    
    self.pan = scrollView.panGestureRecognizer;
    [self.pan addObserver:self forKeyPath:FQDKeyPathScrollViewPanState options:options context:nil];
}

- (void)removeObservers:(UIView *)oldSuperview;
{
    UIScrollView *scrollView = (UIScrollView *)oldSuperview;
    [scrollView removeObserver:self forKeyPath:FQDKeyPathScrollViewContentOffset];
    
    [self.pan removeObserver:self forKeyPath:FQDKeyPathScrollViewPanState];
    self.pan = nil;
}

@end
